package com.innovation.ic.b1b.monitor.base.handler;

import com.google.common.base.Strings;
import com.innovation.ic.b1b.monitor.base.mapper.ElasticsearchAvailableJobLogMapper;
import com.innovation.ic.b1b.monitor.base.mapper.ElasticsearchAvailableJobMapper;
import com.innovation.ic.b1b.monitor.base.mapper.ElasticsearchMapper;
import com.innovation.ic.b1b.monitor.base.model.Elasticsearch;
import com.innovation.ic.b1b.monitor.base.model.ElasticsearchAvailableJob;
import com.innovation.ic.b1b.monitor.base.model.ElasticsearchAvailableJobLog;
import com.innovation.ic.b1b.monitor.base.pojo.constant.JobDataMapConstant;
import com.innovation.ic.b1b.monitor.base.pojo.enums.ActiveEnum;
import com.innovation.ic.b1b.monitor.base.value.TimeSetConfig;
import lombok.SneakyThrows;
import org.apache.http.HttpHost;
import org.apache.http.client.methods.HttpHead;
import org.elasticsearch.client.*;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
import javax.annotation.Resource;
import java.io.IOException;
import java.sql.Date;
import java.util.Random;

/**
 * @desc   监控elasticsearch可用任务处理类
 * @author linuo
 * @time   2023年3月24日10:22:10
 */
public class ElasticsearchAvailableJobHandler extends QuartzJobBean {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Resource
    private HandlerHelper handlerHelper;

    @Resource
    private TimeSetConfig timeSetConfig;

    @Resource
    private ElasticsearchMapper elasticsearchMapper;

    @Resource
    private ElasticsearchAvailableJobMapper elasticsearchAvailableJobMapper;

    @Resource
    private ElasticsearchAvailableJobLogMapper elasticsearchAvailableJobLogMapper;

    @SneakyThrows
    @Override
    protected void executeInternal(org.quartz.JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("开始执行监控elasticsearch可用任务处理类");

        // 获取1-5间的随机数
        int min = 1;
        int max = 5;
        Random random = new Random();
        int s = random.nextInt(max) % (max - min + 1) + min;
        int sleepTime = s * 1000;
        log.info("休眠[{}]秒", s);
        Thread.sleep(sleepTime);

        JobDataMap jobDataMap = jobExecutionContext.getMergedJobDataMap();
        int id = jobDataMap.getInt(JobDataMapConstant.ID);
        ElasticsearchAvailableJob elasticsearchAvailableJob = elasticsearchAvailableJobMapper.selectById(id);
        if(elasticsearchAvailableJob != null){
            Elasticsearch elasticsearch = elasticsearchMapper.selectById(elasticsearchAvailableJob.getElasticsearchId());
            if(elasticsearch != null){
                String esName = elasticsearch.getName();

                // 存活状态
                Integer active = ActiveEnum.NO.getCode();

                // elasticsearch可用任务日志表
                ElasticsearchAvailableJobLog elasticsearchAvailableJobLog = new ElasticsearchAvailableJobLog();
                elasticsearchAvailableJobLog.setElasticsearchAvailableJobId(id);
                elasticsearchAvailableJobLog.setStartTime(new Date(System.currentTimeMillis()));

                if(Strings.isNullOrEmpty(elasticsearch.getIp()) || Strings.isNullOrEmpty(elasticsearch.getPort())){
                    String message = esName + "配置信息不完整,无法检测是否存活";
                    log.info(message);
                    elasticsearchAvailableJobLog.setDescription(message);
                }else{
                    // 判断es是否可用
                    String message = judgeEsIfActive(elasticsearch);
                    if(message == null){
                        active = ActiveEnum.YES.getCode();
                    }else{
                        elasticsearchAvailableJobLog.setDescription(message);
                    }
                }

                elasticsearchAvailableJobLog.setActive(active);
                int insert = elasticsearchAvailableJobLogMapper.insert(elasticsearchAvailableJobLog);
                if(insert > 0){
                    log.info("elasticsearch可用任务日志插入成功");

                    if(active.intValue() == ActiveEnum.NO.getCode().intValue()){
                        // 发送邮件
                        String message = esName + "出现异常，原因：" + elasticsearchAvailableJobLog.getDescription() + "，请查看Elasticsearch状态并及时进行异常处理。";
                        handlerHelper.sendEmail(elasticsearchAvailableJob.getAlarmEmail(), message, null);
                    }
                }
            }else{
                log.info("elasticsearch配置信息不存在,无法执行任务");
            }
        }else{
            log.info("监控elasticsearch可用任务id=[{}]的数据不存在,无法执行任务", id);
        }
    }

    /**
     * 判断es是否可用
     * @param elasticsearch es配置信息
     * @return 返回结果
     */
    private String judgeEsIfActive(Elasticsearch elasticsearch) throws InterruptedException, IOException {
        String message = null;

        RestHighLevelClient client = null;

        try {
            log.info("第一次执行监控elasticsearch是否可用任务");
            client = initRestClient(elasticsearch.getIp(), Integer.parseInt(elasticsearch.getPort()));
            Request request = new Request(HttpHead.METHOD_NAME, elasticsearch.getEsIndex());
            Response response = client.getLowLevelClient().performRequest(request);
            if (response != null && response.getStatusLine().getStatusCode() == 200) {
                log.info("连接ES成功,esId:[{}]", elasticsearch.getId());
            }else{
                message = "索引" + elasticsearch.getEsIndex() + "连接失败";
            }
        }catch (Exception e){
            if(client != null){
                client.close();
                client = null;
            }

            log.warn("第一次执行监控elasticsearch是否可用任务出现问题,原因:[{}],[{}]秒后重试连接", e.toString(), timeSetConfig.getRetryWaitTime());
            Thread.sleep(timeSetConfig.getRetryWaitTime() * 1000);

            try {
                log.info("第二次执行监控elasticsearch是否可用任务");
                client = initRestClient(elasticsearch.getIp(), Integer.parseInt(elasticsearch.getPort()));
                Request request = new Request(HttpHead.METHOD_NAME, elasticsearch.getEsIndex());
                Response response = client.getLowLevelClient().performRequest(request);
                if (response != null && response.getStatusLine().getStatusCode() == 200) {
                    log.info("连接ES成功,esId:[{}]", elasticsearch.getId());
                }else{
                    message = "索引" + elasticsearch.getEsIndex() + "连接失败";
                }
            }catch (Exception e1){
                if(client != null){
                    client.close();
                    client = null;
                }

                log.warn("第二次执行监控elasticsearch是否可用任务出现问题,原因:[{}],{}秒后重试连接", e1.toString(), timeSetConfig.getRetryWaitTime());
                Thread.sleep(timeSetConfig.getRetryWaitTime() * 1000);

                try {
                    log.info("第三次执行监控elasticsearch是否可用任务");
                    client = initRestClient(elasticsearch.getIp(), Integer.parseInt(elasticsearch.getPort()));
                    Request request = new Request(HttpHead.METHOD_NAME, elasticsearch.getEsIndex());
                    Response response = client.getLowLevelClient().performRequest(request);
                    if (response != null && response.getStatusLine().getStatusCode() == 200) {
                        log.info("连接ES成功,esId:[{}]", elasticsearch.getId());
                    }else{
                        message = "索引" + elasticsearch.getEsIndex() + "连接失败";
                    }
                }catch (Exception e2){
                    if(client != null){
                        client.close();
                        client = null;
                    }

                    log.warn("第三次执行监控elasticsearch是否可用任务出现问题,原因:[{}],elasticsearch连接失败", e2.toString());
                    message = e2.toString();
                }
            }
        }finally {
            if(client != null){
                client.close();
                client = null;
            }
        }

        if(!Strings.isNullOrEmpty(message)){
            if(message.length() > 100){
                message = message.substring(0, 100);
            }
        }

        return message;
    }

    /**
     * 初始化Es客户端连接
     * @param url 地址
     * @param port 端口
     * @return 返回连接客户端内容
     */
    public RestHighLevelClient initRestClient(String url, int port) {
        RestClientBuilder builder = RestClient.builder(new HttpHost(url, port, "http"))
                .setRequestConfigCallback(requestConfigBuilder -> requestConfigBuilder
                        .setConnectTimeout(10000)
                        .setSocketTimeout(5000)
                );
        return new RestHighLevelClient(builder);
    }
}